﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Reflection;
using Alex.Library.Web.Ajax.Exceptions;
using System.Collections.Specialized;

namespace Alex.Library.Web.Ajax
{
    /// <summary>
    /// Ajax请求执行方法调用类
    /// </summary>
    public class WAjaxInvoker
    {
        private HttpContext _WebContext;
        /// <summary>
        /// 获取当前Http上下文
        /// </summary>
        public HttpContext WebContext
        {
            get { return _WebContext; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="webContext">当前Http上下文</param>
        public WAjaxInvoker(HttpContext webContext)
        {
            this._WebContext = webContext;
        }

        /// <summary>
        /// 调用Ajax方法处理请求
        /// </summary>
        /// <param name="clearResponse">是否以新的响应内容输出客户端</param>
        /// <param name="responseBuffer">是否缓存响应内容</param>
        /// <param name="charset">响应内容的字符集</param>
        /// <param name="contentEncoding">响应内容的字符编码</param>
        public void Invoke(bool clearResponse, bool responseBuffer,
            string charset, Encoding contentEncoding)
        {                        
            //根据返回值类型生成结果
            ISerializeHelper sh = null;
            switch (this._WebContext.Request["__returntype"])
            {
                case "xml":
                    sh = SerializeHelper.Eval(SerializeType.XML);
                    this._WebContext.Response.ContentType = "text/xml";
                    break;
                case "json":
                    sh = SerializeHelper.Eval(SerializeType.JSON);
                    this._WebContext.Response.ContentType = "text/html";
                    break;
                case "html":
                    sh = SerializeHelper.Eval(SerializeType.Text);
                    this._WebContext.Response.ContentType = "text/html";
                    break;
            }
            if (clearResponse)
            {
                this._WebContext.Response.Clear();
            }
            this._WebContext.Response.Buffer = responseBuffer;
            this._WebContext.Response.Charset = charset;
            this._WebContext.Response.ContentEncoding = contentEncoding;

            string responseBody = String.Empty;
            try
            {
                string assemblyName = this._WebContext.Request["__assembly"];
                if (String.IsNullOrEmpty(assemblyName))
                {
                    throw new AssemblyNotFoundException();
                }
                string className = this._WebContext.Request["__class"];
                if (String.IsNullOrEmpty(className))
                {
                    throw new ClassNotFoundException();
                }
                string methodName = this._WebContext.Request["__method"];
                if (String.IsNullOrEmpty(methodName))
                {
                    throw new MissingMethodException();
                }
                //由于得到的参数值可能为null，所以不能通过parameters的value.gettype获取参数类型
                Type[] paramTypes;
                Dictionary<string, object> parameters = GetMethodParamters(out paramTypes);

                Type classType = Type.GetType(className + "," + assemblyName);                
                object obj = System.Activator.CreateInstance(classType);
                MethodInfo method = ObjectMethodHandler.GetMethodByName(classType, methodName, paramTypes);                
                //判断HttpRequest请求方式是否被该方法接受
                if (!AllowRequest(method))
                {
                    throw new InvalidOperationException("调用的方法不接受" 
                        + this._WebContext.Request.RequestType + "请求方式.");
                }
                //调用方法
                object data = method.Invoke(obj, parameters.Values.ToArray());
                
                //如果method是构造函数或者无返回值，那么data为null
                if (null == data)
                {
                    responseBody = null;
                }
                else
                {
                    responseBody = sh.Serialize(data);
                }
            }
            catch (Exception ex)
            {
                this._WebContext.Response.StatusCode = 500;
                responseBody = ex.ToString();
            }

            this._WebContext.Response.Write(responseBody);
            this._WebContext.Response.Flush();
        }

        /// <summary>
        /// 获取调用方法的全部参数
        /// </summary>
        /// <returns>参数名/参数值列表</returns>
        protected Dictionary<string, object> GetMethodParamters(out Type[] types)
        {
            types = System.Type.EmptyTypes;
            NameValueCollection nvs = null;
            if (this._WebContext.Request.RequestType.Equals("GET", StringComparison.CurrentCultureIgnoreCase))
            {
                nvs = this._WebContext.Request.QueryString;
            }
            else
            {
                nvs = this._WebContext.Request.Form;
            }
            if (nvs != null)
            {
                //屏蔽以下这些参数中的特殊键
                string[] specKey = new string[] 
                {   
                    "__assembly", 
                    "__class", 
                    "__method",
                    "__returntype", 
                    "__temp" 
                };
                var keySets = from key in nvs.AllKeys
                              where Array.IndexOf(specKey, key) == -1
                              select key;
                Dictionary<string, object> paramters = new Dictionary<string, object>();
                if (keySets.Count() > 0)
                {
                    types = new Type[keySets.Count()];
                }
                int i = 0;
                foreach (string key in keySets)
                {
                    string s = this._WebContext.Server.UrlDecode(nvs.Get(key));
                    string[] valueAndType = s.Split(new string[1] { "::" }, StringSplitOptions.None);                    
                    Type type = TypeFactory.CreateTypeByName(valueAndType[1]);
                    ISerializeHelper jsonHelper = SerializeHelper.Eval(SerializeType.JSON);                    
                    paramters[key] = jsonHelper.Deserialize(valueAndType[0], type);
                    if (types != null)
                    {
                        types[i++] = type;
                    }
                }
                return paramters;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 判断方法是否允许以当前请求方式(GET,POST)进行调用
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        protected bool AllowRequest(MethodInfo method)
        {
            object[] objs = method.GetCustomAttributes(typeof(AjaxMethodAttribute), false);
            if (objs != null && objs.Length > 0)
            {
                AjaxMethodAttribute attr = objs[0] as AjaxMethodAttribute;
                return attr.RequestType == HttpRequestType.ALL || 
                    attr.RequestType.ToString().Equals(this._WebContext.Request.RequestType, StringComparison.CurrentCultureIgnoreCase);
            }
            return false;
        }
    }
}
